package Client;
public final class ByteStream {
    private int offset;
    private int length;
    private byte[] buffer;
    private int opcodeStart;

    public ByteStream(int size) {
        buffer = new byte[size];
    }

    public void checkCapacity(int length) {
        if (length >= buffer.length) {
            byte[] newBuffer = new byte[length * 2];
            System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);
            buffer = newBuffer;
        }
    }

    public int offset() {
        return offset;
    }

    public void setOffset(int offset) {
        this.offset = offset;
    }

    public int length() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public byte[] buffer() {
        return buffer;
    }

    public int remaining() {
        return offset < length && offset < buffer.length ? length - offset : 0;
    }

    public int getByte() {
        return remaining() > 0 ? buffer[offset++] : 0;
    }

    public int getUnsignedByte() {
        return getByte() & 0xff;
    }

    public int getShort() {
        int i = (getUnsignedByte() << 8) + getUnsignedByte();
        if (i > 32767) {
            i -= 0x10000;
        }
        return i;
    }

    public int getUnsignedShort() {
        return (getUnsignedByte() << 8) + getUnsignedByte();
    }

    public int getInt() {
        return (getUnsignedByte() << 24) + (getUnsignedByte() << 16) + (getUnsignedByte() << 8) + getUnsignedByte();
    }

    public long getLong() {
        long l = getInt() & 0xffffffffL;
        long l1 = getInt() & 0xffffffffL;
        return (l << 32) + l1;
    }

    public String getString() {
        String s = "";
        int b;
        while ((b = getByte()) != 0) {
            s += (char) b;
        }
        return s;
    }

    public int getSmart() {
        int i = buffer[offset] & 0xff;
        if (i < 128) {
            return getUnsignedByte();
        } else {
            return getUnsignedShort() - 32768;
        }
    }

    public int getOpcode() {
        return getUnsignedByte();
    }

    public void addByte(int i) {
        addByte(i, offset++);
    }

    public void addByte(int i, int position) {
        checkCapacity(position);
        buffer[position] = (byte) i;
    }

    public void addShort(int i) {
        addByte(i >> 8);
        addByte(i);
    }

    public void addInt(int i) {
        addByte(i >> 24);
        addByte(i >> 16);
        addByte(i >> 8);
        addByte(i);
    }

    public void addLong(long l) {
        addByte((int) (l >> 56));
        addByte((int) (l >> 48));
        addByte((int) (l >> 40));
        addByte((int) (l >> 32));
        addByte((int) (l >> 24));
        addByte((int) (l >> 16));
        addByte((int) (l >> 8));
        addByte((int) l);
    }

    public void addString(String s) {
        checkCapacity(offset + s.length() + 1);
        System.arraycopy(s.getBytes(), 0, buffer, offset, s.length());
        offset += s.length();
        addByte(0);
    }

    public void addSmart(int i) {
        i &= 0xffff;
        if (i >= 128 && i <= 32768) {
            addShort(i + 32768);
        } else {
            addByte(i);
        }
    }

    public void addOpcode(int id) {
        addByte(id);
    }

    public void addOpcodeVarByte(int id) {
        addOpcode(id);
        addByte(0);
        opcodeStart = offset - 1;
    }

    public void addOpcodeVarShort(int id) {
        addOpcode(id);
        addShort(0);
        opcodeStart = offset - 2;
    }

    public void endOpcodeVarByte() {
        addByte(offset - (opcodeStart + 1), opcodeStart);
    }

    public void endOpcodeVarShort() {
        int size = offset - (opcodeStart + 2);
        addByte(size >> 8, opcodeStart++);
        addByte(size, opcodeStart);
    }

    public void addBytes(byte[] b, int offset, int length) {
        checkCapacity(this.length + length);
        System.arraycopy(b, offset, buffer, this.length, length);
        this.length += length;
    }

    public byte[] copy() {
        byte[] newBuffer = new byte[offset];
        System.arraycopy(buffer, 0, newBuffer, 0, offset);
        return newBuffer;
    }
}